package cn.com.duiba.millionaire.center.biz.remoteservice;

import cn.com.duiba.developer.center.api.domain.dto.AppSimpleDto;
import cn.com.duiba.developer.center.api.remoteservice.RemoteAppService;
import cn.com.duiba.millionaire.center.api.dto.MillionaireConsumerAccountRecordDto;
import cn.com.duiba.millionaire.center.api.dto.MillionaireConsumerDto;
import cn.com.duiba.millionaire.center.api.dto.MillionaireWithdrawDto;
import cn.com.duiba.millionaire.center.api.enums.AccountTypeEnum;
import cn.com.duiba.millionaire.center.api.enums.AppConfigProNameEnum;
import cn.com.duiba.millionaire.center.api.enums.WithdrawStatusEnum;
import cn.com.duiba.millionaire.center.api.param.WithdrawApplyParam;
import cn.com.duiba.millionaire.center.api.remoteservice.RemoteMillionaireWithdrawService;
import cn.com.duiba.millionaire.center.api.result.WithdrawApplyResult;
import cn.com.duiba.millionaire.center.biz.exception.BusinessException;
import cn.com.duiba.millionaire.center.biz.exception.ErrorCode;
import cn.com.duiba.millionaire.center.biz.service.*;
import cn.com.duiba.paycenter.client.RpcResult;
import cn.com.duiba.paycenter.client.WithdrawServiceClient;
import cn.com.duiba.paycenter.enums.WithdrawBizTypeEnum;
import cn.com.duiba.paycenter.params.WithdrawParams;
import cn.com.duiba.paycenter.result.WithdrawResult;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * Created by xiaoxuda on 2018/2/1.
 */
@RestController
public class RemoteMillionaireWithdrawServiceImpl implements RemoteMillionaireWithdrawService {
    private static final Logger LOGGER = LoggerFactory.getLogger(RemoteMillionaireWithdrawServiceImpl.class);
    @Autowired
    private MillionaireWithdrawService millionaireWithdrawService;
    @Autowired
    private MillionaireConsumerService millionaireConsumerService;
    @Autowired
    private ConsumerAccountRecordService consumerAccountRecordService;
    @Autowired
    private WithdrawServiceClient withdrawServiceClient;
    @Autowired
    private RemoteAppService remoteAppService;
    @Autowired
    private WithdrawAlipayService withdrawAlipayService;
    @Autowired
    private MillionaireAppConfigService millionaireAppConfigService;

    private static final Float rate = 0.00f;//提现手续费暂不收取

    @Override
    public WithdrawApplyResult applyWithdraw(WithdrawApplyParam param) {
        try {
            AppSimpleDto app = remoteAppService.getSimpleApp(param.getAppId()).getResult();

            MillionaireWithdrawDto withdraw = createWithdrawApply(param);
            if (withdraw.getId() == null) {
                return WithdrawApplyResult.failResult("创建提现请求失败");
            }
            deductConsumerAccount(withdraw);
            deductDeveloperAccount(withdraw, app);
            withdrawAlipayService.applyAlipayTransfer(withdraw, app);
            return WithdrawApplyResult.successResult(withdraw.getId());
        } catch (BusinessException e) {
            LOGGER.info("提现失败， failMsg={}", e.getMessage());
            return WithdrawApplyResult.failResult(e.getMessage());
        } catch (Exception e) {
            LOGGER.error("提现失败", e);
            return WithdrawApplyResult.failResult(e.getMessage());
        }
    }

    @Override
    public WithdrawApplyResult applyWithdrawAllBalance(WithdrawApplyParam param) {
        try {
            AppSimpleDto app = remoteAppService.getSimpleApp(param.getAppId()).getResult();

            MillionaireWithdrawDto withdraw = createWithdrawApplyAllBalance(param);
            if (withdraw.getId() == null) {
                return WithdrawApplyResult.failResult("创建提现请求失败");
            }
            deductConsumerAccount(withdraw);
            deductDeveloperAccount(withdraw, app);
            withdrawAlipayService.applyAlipayTransfer(withdraw, app);
            return WithdrawApplyResult.successResult(withdraw.getId());
        } catch (BusinessException e) {
            LOGGER.info("提现失败， failMsg={}", e.getMessage());
            return WithdrawApplyResult.failResult(e.getMessage());
        } catch (Exception e) {
            LOGGER.error("提现失败", e);
            return WithdrawApplyResult.failResult(e.getMessage());
        }
    }

    @Override
    public WithdrawApplyResult applyWithdrawAllBalanceWithShareCode(Long consumerId, Long appId, String shareCode) {
        try {
            AppSimpleDto app = remoteAppService.getSimpleApp(appId).getResult();

            MillionaireWithdrawDto withdraw = createWithdrawApplyAllBalance(consumerId, app.getId(), shareCode);
            if (withdraw.getId() == null) {
                return WithdrawApplyResult.failResult("创建提现请求失败");
            }
            deductConsumerAccount(withdraw);
            deductDeveloperAccount(withdraw, app);
            withdrawAlipayService.applyAlipayTransfer(withdraw, app);
            return WithdrawApplyResult.successResult(withdraw.getId());
        } catch (BusinessException e) {
            LOGGER.info("提现失败， failMsg={}", e.getMessage());
            return WithdrawApplyResult.failResult(e.getMessage());
        } catch (Exception e) {
            LOGGER.error("提现失败", e);
            return WithdrawApplyResult.failResult(e.getMessage());
        }
    }

    private MillionaireWithdrawDto createWithdrawApply(WithdrawApplyParam param) {
        if (param.getAmount() < 10) {
            throw new BusinessException("提现金额最低0.1元");
        }
        MillionaireWithdrawDto withdraw = new MillionaireWithdrawDto();
        withdraw.setConsumerId(param.getConsumerId());
        withdraw.setAppId(param.getAppId());
        withdraw.setAmount(param.getAmount().intValue());
        withdraw.setDevAmount((int) Math.ceil(withdraw.getAmount() * (1 + rate)));
        withdraw.setWithdrawStatus(WithdrawStatusEnum.INIT);
        withdraw.setEnvIp(param.getReqEnv().getEnvIp());
        withdraw.setEnvUa(param.getReqEnv().getEnvDevices());
        withdraw.setId(millionaireWithdrawService.insert(withdraw));
        return withdraw;
    }

    /**
     * 提现最小单位1角
     * @param param
     * @return
     */
    private MillionaireWithdrawDto createWithdrawApplyAllBalance(WithdrawApplyParam param) {
        MillionaireConsumerDto millionaireConsumer = millionaireConsumerService.findByConsumerIdNoCache(
                param.getConsumerId(), param.getAppId(), null);
        if (millionaireConsumer.getAccount() < 10) {
            throw new BusinessException("提现金额最低0.1元");
        }
        MillionaireWithdrawDto withdraw = new MillionaireWithdrawDto();
        withdraw.setConsumerId(millionaireConsumer.getConsumerId());
        withdraw.setAppId(millionaireConsumer.getAppId());
        withdraw.setAmount(millionaireConsumer.getAccount().intValue());
        withdraw.setDevAmount((int) Math.ceil(withdraw.getAmount() * (1 + rate)));
        withdraw.setWithdrawStatus(WithdrawStatusEnum.INIT);
        withdraw.setEnvIp(param.getReqEnv().getEnvIp());
        withdraw.setEnvUa(param.getReqEnv().getEnvDevices());
        withdraw.setId(millionaireWithdrawService.insert(withdraw));
        return withdraw;
    }

    /**
     * 提现最小单位1角
     *
     * @param consumerId
     * @param appId
     * @return
     */
    private MillionaireWithdrawDto createWithdrawApplyAllBalance(Long consumerId, Long appId, String shareCode) {
        MillionaireConsumerDto millionaireConsumer = millionaireConsumerService.findByConsumerIdNoCache(consumerId, appId, shareCode);
        if (millionaireConsumer.getAccount() < 10) {
            throw new BusinessException("提现金额最低0.1元");
        }
        MillionaireWithdrawDto withdraw = new MillionaireWithdrawDto();
        withdraw.setConsumerId(millionaireConsumer.getConsumerId());
        withdraw.setAppId(millionaireConsumer.getAppId());
        withdraw.setAmount(millionaireConsumer.getAccount().intValue());
        withdraw.setDevAmount((int) Math.ceil(withdraw.getAmount() * (1 + rate)));
        withdraw.setWithdrawStatus(WithdrawStatusEnum.INIT);
        withdraw.setId(millionaireWithdrawService.insert(withdraw));
        return withdraw;
    }

    //事务控制
    private void deductConsumerAccount(MillionaireWithdrawDto withdraw) {
        Exception processException = null;
        try {
            MillionaireConsumerDto millConsumer = millionaireConsumerService.select4Update(withdraw.getConsumerId());
            if (millConsumer == null) {
                throw new BusinessException("锁定账户失败");
            }
            if (millConsumer.getAccount() < withdraw.getAmount()) {
                throw new BusinessException("余额不足");
            }

            MillionaireConsumerAccountRecordDto record = consumerAccountRecordService
                    .findByConsumerIdAndBizTypeAndBizId(withdraw.getConsumerId(),
                            AccountTypeEnum.WITHDRAW_ONLINE.getCode(), String.valueOf(withdraw.getId()));
            if (record != null) {
                throw new BusinessException(ErrorCode.RE_SUBMIT);
            }
            record = new MillionaireConsumerAccountRecordDto();
            record.setConsumerId(withdraw.getConsumerId());
            record.setAppId(withdraw.getAppId());
            record.setBizType(AccountTypeEnum.WITHDRAW_ONLINE.getCode());
            record.setBizId(String.valueOf(withdraw.getId()));
            record.setAmount(withdraw.getAmount());
            record = consumerAccountRecordService.insert(record);
            if (record.getId() == null) {
                throw new BusinessException("创建账户变更记录失败");
            }

            boolean deductRet = millionaireConsumerService.withdrawCash(record.getConsumerId(), record.getAmount());
            if (!deductRet) {
                throw new BusinessException("用户扣款失败");
            }
        } catch (Exception e) {
            processException = e;
            throw e;
        } finally {
            if (processException != null) {
                //TODO transaction.setRollbackOnly
            }
            //TODO transaction submit
            if (processException == null) {
                millionaireWithdrawService.update2UserPayed(withdraw.getId());
            } else if (!(processException instanceof BusinessException
                    && ErrorCode.RE_SUBMIT.equals(((BusinessException) processException).getErrorCode()))) {
                String failMsg = processException.getMessage();
                if(StringUtils.length(failMsg) > 255){
                    failMsg = failMsg.substring(0, 255);
                }
                millionaireWithdrawService.update2Fail(withdraw.getId(),failMsg);
            }
        }
    }

    private void deductDeveloperAccount(MillionaireWithdrawDto withdraw, AppSimpleDto app) {
        MillionaireWithdrawDto draw = millionaireWithdrawService.findById(withdraw.getId());
        if (!WithdrawStatusEnum.USER_PAYED.equals(draw.getWithdrawStatus())) {
            throw new BusinessException("申请状态错误");
        }
        Long appId = draw.getAppId();
        List<Long> appIds = millionaireAppConfigService.findAppIdByPropName(AppConfigProNameEnum.ALIPAY_DUIBA.getPropName());
        //不需要扣开发者资金,由兑吧自己支付
        if(appIds.contains(appId)){
            millionaireWithdrawService.update2DevPayed(draw.getId());
            LOGGER.info("支付宝提现-奖金由兑吧提供,不扣开发者资金,appId={}",appId);
            return;
        }
        WithdrawParams withdrawParams = new WithdrawParams();
        withdrawParams.setDeveloperId(app.getDeveloperId());
        withdrawParams.setAppId(appId);
        withdrawParams.setConsumerId(draw.getConsumerId());
        withdrawParams.setBizType(WithdrawBizTypeEnum.USER_MILLIONAIRE);
        withdrawParams.setBizId(draw.getId());
        withdrawParams.setMoney(draw.getDevAmount().longValue());
        withdrawParams.setMemo(app.getName() + "-冲顶大会奖金");
        RpcResult<WithdrawResult> rpcResult = withdrawServiceClient.userWithdrawCashApply(withdrawParams);
        if (!rpcResult.isOpSuccess() || !Boolean.TRUE.equals(rpcResult.getResult().getSuccess())) {
            millionaireConsumerService.withdrawCashPayback(draw.getConsumerId(), draw.getAmount());
            millionaireWithdrawService.update2Fail(draw.getId(), "扣app余额失败");
            LOGGER.warn("扣开发者app余额失败,appId={},consumerId={},amount={}",appId,draw.getConsumerId(),draw.getAmount());
            throw new BusinessException("提现失败,已提交系统处理,请耐心等待");
        }
        millionaireWithdrawService.update2DevPayed(draw.getId());
    }

    @Override
    public MillionaireWithdrawDto findById(Long id) {
        return millionaireWithdrawService.findById(id);
    }

    @Override
    public List<MillionaireWithdrawDto> batchFindByConsumerId(Long consumerId) {
        return millionaireWithdrawService.batchFindByConsumerId(consumerId);
    }

    @Override
    public Long countWithdrawAmount(Long consumerId) {
        return millionaireWithdrawService.countWithdrawAmount(consumerId);
    }
}
